I decided to write up a little tutorial that explains the CODEPUNCH command since it's the most complicated and most powerful I-Patch¬ command. It requires a bit of preparation to use, whereas most I-Patch¬ commands require either some hex data or a resource of some kind or other.
For CODEPUNCH, you will need information about the segment of code that you're punching into, a CODE resource for the CODEPUNCH code to call, and the corresponding info for the script.
The function of CODEPUNCH is to place a 26 byte segment of code into an existing CODE resource (or any resource that has executable code in it) that has the function of loading another specified resource, calling it with a JSR, and then resuming code execution immediately following the 'punched-in' code. This allows the script writer to have any application execute additional code not originally in the application without disturbing the original application at all.
As you will see in this example, some code will be added to the Sample App (in the Sample App folder included in the I-Patch¬ package) that will load up a sound resource and play that sound when the Sample App starts up. This function was not included in the original application, but will be 'punched-in' by the script.
In preparation for the CODEPUNCH, we need a debugger such as MacsBug or (my favorite) TMON Professional.
A good place to punch in my code is at the very beginning of the application where it does its initialization. There's space here to put in my 26 bytes of CODEPUNCH code. The code that is 'punched-in' is the following:
CLR.L -(SP) ; save 4 bytes on stack for res handle
MOVE.L #$00000000, -(SP) ; push the ResType to load
MOVE.W #$0000, -(SP) ; push the ResID to load
_GetResource ; get the resource from the res file
MOVEA.L (SP), A0 ; put the handle to the resource in reg A0
_HLock ; lock the resource
MOVEA.L (A0), A1 ; put the address of the resource data in A1
JSR (A1) ; call the loaded resource's code
_HUnlock ; Unlock the resource
_ReleaseResource ; release the memory occupied by it
This occupies 26 bytes of space, and thus I must find 26 bytes of code that are 'safe' to punch in over (code that can be included in the loaded code resource and will execute
there safely). This requires some examination of the object code of the application that will be 'punched-into', and the debugger is great for this.
Since I want to look at the application's initialization code, I set my debugger up to do a trap intercept at _InitGraf since this is commonly the first trap executed by any application as part of its initialization. I then run the Sample App and let it break. Examining the code around the program counter after the break reveals the following segment of code:
005CB51E:'CODE'¿$0002─$125E+$01EE PEA `FFEE(A5)
005CB522:'CODE'¿$0002─$125E+$01F2 _InitGraf
005CB524:'CODE'¿$0002─$125E+$01F4 _InitWindows
005CB526:'CODE'¿$0002─$125E+$01F6 _InitFonts
005CB528:'CODE'¿$0002─$125E+$01F8 _InitMenus
005CB52A:'CODE'¿$0002─$125E+$01FA _TEInit
005CB52C:'CODE'¿$0002─$125E+$01FC CLR.L -(A7)
005CB52E:'CODE'¿$0002─$125E+$01FE _InitDialogs
005CB530:'CODE'¿$0002─$125E+$0200 _InitCursor
005CB532:'CODE'¿$0002─$125E+$0202 _MoreMasters
005CB534:'CODE'¿$0002─$125E+$0204 _MoreMasters
005CB536:'CODE'¿$0002─$125E+$0206 _MoreMasters
005CB538:'CODE'¿$0002─$125E+$0208 _MoreMasters
005CB53A:'CODE'¿$0002─$125E+$020A _MoreMasters
005CB53C:'CODE'¿$0002─$125E+$020C _MoreMasters
005CB53E:'CODE'¿$0002─$125E+$020E CLR.B `FFFE(A5)
005CB542:'CODE'¿$0002─$125E+$0212 RTS
I can see that there is room to put my 26 bytes of CODEPUNCH code here, and I choose to put it immediately following the _InitGraf trap. When I-Patch¬ codes the codepunch, it will place it's codepunch code over the top of _InitWindows, _InitFonts, etc.. all the way over all of the calls to _MoreMasters and right up to the CLR.B instruction. It is not the case that the last byte of the 26 cuts an existing instruction in half (the _MoreMasters trap instruction is only 2 bytes, but another instruction, such as a MOVE.L immediate for example, could be several words long). If it is the case that an existing instruction is only partially overwritten by the last bytes of the CODEPUNCH code, there will be a garbage instruction immediately following the last instruction of the CODEPUNCH code (the _ReleaseResource trap). When this happens, it is necessary to put one or more 'NOP' (No OPeration) instructions there to overwrite the rest of the clobbered instruction. This ensures that program execution will continue after the CODEPUNCH code without encountering illegal instructions. There is a provision in the calling syntax of the CODEPUNCH script command to tell I-Patch¬ to write a number of NOP instructions immediately after writing the CODEPUNCH code into the application code. Since there are no clobbered instructions in this example, this won't be necessary--the 26 bytes fit perfectly.
After the CODEPUNCH command puts its code into this sequence of instructions, they look like this:
00596FC6:'CODE'¿$0002─$12BC+$01EE PEA `FFEE(A5)
00596FCA:'CODE'¿$0002─$12BC+$01F2 _InitGraf
00596FCC:'CODE'¿$0002─$12BC+$01F4 CLR.L -(A7) ; <-- beginning of punched code
00596FE4:'CODE'¿$0002─$12BC+$020C _ReleaseResource ; <-- last punched instr.
00596FE6:'CODE'¿$0002─$12BC+$020E CLR.B `FFFE(A5)
00596FEA:'CODE'¿$0002─$12BC+$0212 RTS
As you can see, control passes immediately to the CLR.B instruction. What was clobbered by this code is the rest of the initialization: the _InitWindows, _InitFonts, all the way through all of the _MoreMaster's calls. Since this code no longer exists and will not be executed, it will have to be included in the loaded code resource. Examining the punched in code indicates that the resource that the _GetResource trap will load is a resource of type 'pnch' and resource ID #1234. This is information that I supplied the CODEPUNCH command inside the I-Patch¬ script. These could be any resource type and any res ID that I wanted, so long as I included that resource as part of the I-Patch¬ script.
But, I'm getting ahead of myself here. Back to the sequence of operations. The first thing that the user must do is look at the application's code for the place to overwrite with the CODEPUNCH code. Examination of the first dump above indicates that at hex offset $01F4 from the beginning of CODE resource ID#2 there is a good place to put the punched code. I write down that info.. (CODE ID #2, offset $01F4) because I'll need it later when I'm writing the script itself.
Next, it's my job to write a CODE resource that will be loaded. I used THINK C to compile my code. The source file is included in the I-Patch¬ package. It's my responsibility to remember what code was overwritten and include it in my CODE resource somewhere. Since the initialization code for the Sample App was overwritten, I will include this at the beginning of my code resource (which will be of type 'pnch' and ID #1234 as we now know).
The code I wrote looks like this:
void main()
{
Handle myHand; /* a temporary handle for my punched-in code */
/* This the overwrite code--that is, the code that was overwritten by the CODEPUNCH
code when I-Patch executed the CODEPUNCH operation. The code that it replaced
the application's old code with has the function of loading and executing a
resource. This is that resource, and since some code was overwritten, I will
have to include that code in this resource so that it DOES get executed.
*/
/* this is the code that was overwritten by the 'punched-in' code */
InitWindows();
InitFonts();
InitMenus();
TEInit();
InitDialogs(NULL);
InitCursor();
MoreMasters();
MoreMasters();
MoreMasters();
MoreMasters();
MoreMasters();
MoreMasters();
/* now that the application's original code has been executed, */
/* I can do what I want to do */
/* the following code is the code that I want to execute -- my own code. It can be
anything I choose */
/* I choose to load up a sound resource and play it before exiting my code resource and allowing the application to continue */
myHand = GetResource('snd ', 1234); /* load up 'snd ' resource ID 1234 */
SndPlay(NULL, myHand, FALSE); /* play the sound resource */
/* that's all I want this CODE resource to do */
}
So I compile this code and save it as a code resource. I used THINK C with the Custom Headers option on so that THINK C won't put it's code resource header at the beginning of the code resource. A matter of preference, I guess.
Now I'm ready to write the I-Patch¬ script to take advantage of the information I've gathered and the resource I just wrote. I load up I-Patch¬ and start typing:
* This is a script to use the CODEPUNCH command
* to add into the Sample App some code that plays a sound
* I gotta prompt the user to select the Sample App so I can patch it
MESSAGE
"Please select a COPY of the Sample App"
* Now I need to actually open up the application
SELECTFILE
1
'APPL'
* Ok, it's open, now I'm ready to do the CODEPUNCH
* I want to punch into CODE resource #2 at offset
* $01F4 from the beginning . I also want to tell the
* CODEPUNCH command that the punched-in code
* should load a resource of type 'pnch' with ID#1234.
* This is the code resource that I compiled earlier.
CODEPUNCH
'CODE'
2
$1f4
0
'pnch'
1234
* Since my 'pnch' resource loads up a sound that is
* not already in the Sample App, I need to add this
* resource myself. I've already got a sound resource
* made and its ID is 1234 as well. I need to
* Add the 'snd ' resource to the App
ADD
'snd '
1234
'snd '
1234
* That's the last thing I need to do.. all done
END
There, that's the whole script. Of course 2 things are missing from this script file. The script executes the right commands in the right order, but the script must also have the resources that are referenced in the script text loaded into it. I can do this by loading up ResEdit, opening up the files that contain the resources and use the clipboard to copy and paste them, or I can use the I-Patch¬ menu command Add Resource that's on the Script menu to add them from within I-Patch. I will illustrate both methods.
THINK C compiled my 'pnch' resource to a file. I load up ResEdit and open this file. Inside is a resource of type 'pnch' with ID 1234 as I told THINK C. I then select it in the list of 'pnch' resources and do a Copy (Command-C). It's now on the clipboard.
Next, I load up I-Patch¬, open the above I-Patch¬ script file, I click in the Resources Box to select it, and then I do a Paste (Command-V). The resource on the clipboard is now pasted into the I-Patch¬ script. Of course, the resource ID number is wrong since the clipboard doesn't maintain resource ID's--only types. So I can use the Get Info menu command to change the ID to 1234 like the script text requires.
Now I do the same set of operations on the file that contains the 'snd ' resource to add that to the script.
The other method is to use the Get Resource menu command within I-Patch¬. This is a much easier way to do it, but clipboard compatibility was provided for those who prefer to work with ResEdit.
Ok, so within I-Patch¬, I've just typed the script. I can hit Command-R or choose Add Resource from the Script menu. This command will offer a file selection dialog and let you choose the resource file that contains the resource that you want to add. After you open the file, you will be confronted by a list of resource types and resources. Find the one you want and hit the OK button. It will automatically be loaded into the I-Patch¬ script's Resources Box with the correct type and ID. Do this for both the 'pnch' code resource and the sound resource.
That's it. Now the script is complete. This would be a good time to save.
If you execute this script, it will patch the Sample App as required.
A completed script was included in the CODEPUNCH Tutorial Folder that is essentially the same thing as what was generated above. The sound resource and the compiled code resource are also in this folder so that you may follow the above steps to generate your own version of the script. I recommend doing it yourself to get the hang of it.
Make sure to make a copy of the Sample App before applying the patch, just in case there might be an error in the script. It's never a good idea to apply patches of any kind to the original and only copy of something.